#    Rubatom
#    Copyright (c) 2014 Sebastian Dufner
#
#    This file is part of Rubatom.
#
#    Rubatom is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Rubatom is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

module Rubatom
  module VersionedAlgorithm
    class NoSuchVersionError < NameError
      def initialize(version)
        super "Unknown version: #{version}"
      end
    end

    def self.included(other)
      algorithm_name =
          other.to_s.split('::').last.gsub(/([^\A])([[:upper:]]+)/, '\1_\2').downcase

      other.const_set(:Versions, [])
      other.send(:private_constant, :Versions)

      other.send(:define_singleton_method, :version) do |v_spec|
        case v_spec
        when Float
          other.const_get(:Versions) <<
              [[v_spec.floor, v_spec - v_spec.floor], self]
        when String, Symbol
          other.const_get(:Versions) <<
              [v_spec.to_s.split('.').map(&:to_i), self]
        when Array
          other.const_get(:Versions) << [v_spec.map(&:to_i), self]
        end

        nil
      end
      other.send(:private_class_method, :version)

      other.send(:define_singleton_method, algorithm_name) do
          |v_spec = :default|
        versions = other.const_get(:Versions).sort_by &:first

        case v_spec
        when /\A(last|latest|current|newest|default|recommended)\z/
          versions.last[1]
        when /\A(first|oldest|original)\z/
          versions.first[1]
        when Symbol, String
          v_spec2 = v_spec.to_s.split('.').map &:to_i
          index = versions.find_index {|v| (v_spec <=> v) == 1}

          fail NoSuchVersionError, v_spec if index == 0
          return versions.last[1] unless index
          versions[index - 1][1]
        end
      end
    end

  end
end